/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef LIBPANDAFILE_FILE_FORMAT_VERSION_H
#define LIBPANDAFILE_FILE_FORMAT_VERSION_H

#include <array>
#include <string>
#include <sstream>
#include <iomanip>
#include <set>

#include "file.h"
#include "utils/const_value.h"

namespace panda::panda_file {
constexpr uint8_t API_12 = 12;
const std::string SUB_API_VERSION_1 = "beta1";
const std::string SUB_API_VERSION_2 = "beta2";
const std::string DEFAULT_SUB_API_VERSION = SUB_API_VERSION_1;

constexpr std::array<uint8_t, File::VERSION_SIZE> version {<%= Panda::version.split('.').join(', ') %>};
constexpr std::array<uint8_t, File::VERSION_SIZE> minVersion {<%= Panda::min_version.split('.').join(', ') %>};
const std::set<std::array<uint8_t, File::VERSION_SIZE>> incompatibleVersion {
% Panda::incompatible_version.each do |api_version|
    {<%= api_version.split('.').join(', ') %>},
% end
};

std::string GetVersion(const std::array<uint8_t, File::VERSION_SIZE> &version);

bool IsVersionLessOrEqual(const std::array<uint8_t, File::VERSION_SIZE> &currrent_version,
                          const std::array<uint8_t, File::VERSION_SIZE> &target_version);

inline void PrintBytecodeVersion()
{
    std::stringstream ss;

    ss << "Bytecode version " << GetVersion(version) << '\n';
    ss << "Minimum supported bytecode version " << GetVersion(minVersion) << '\n';

    std::cout << ss.str() << std::endl;
}

const std::map<uint8_t, std::array<uint8_t, File::VERSION_SIZE>> api_version_map {
% current_version_raw = Panda::version.split('.')
% current_api = current_version_raw[0]
% current_version = current_version_raw.join(', ')
    {0, {<%= current_version %>}},
% Panda::api_version_map.each do |api_version|
    {<%= api_version[0] %>, {<%= api_version[1].split('.').join(', ') %>}},
% end
    {<%= current_api %>, {<%= current_version %>}}
};

inline void PrintSupportedApi() {
    static constexpr size_t WIDTH = 20;  // set output width
    std::cout << "Supported apis and corresponding file versions:" << std::endl;
    std::cout << std::setw(WIDTH) << "api" << std::setw(WIDTH) << "file version" << std::endl;
    for (const auto &[legal_api, file_version] : api_version_map) {
        if (legal_api == 0) {
            continue;
        } else {
            std::cout << std::setw(WIDTH) << static_cast<uint32_t>(legal_api);
        }
        std::cout << std::setw(WIDTH) << GetVersion(file_version) << std::endl;
    }
}

inline std::optional<const std::array<uint8_t, File::VERSION_SIZE>> GetVersionByApi(uint8_t api, std::string subApi)
{
    if (api == API_12 && (subApi == SUB_API_VERSION_1 || subApi == SUB_API_VERSION_2)) {
        return std::array<uint8_t, File::VERSION_SIZE> {12, 0, 2, 0};
    }

    const auto iter = api_version_map.find(api);
    if (iter == api_version_map.end()) {
        // if there is no corresponding api version, the default branch is current version.
        return version;
    }
    return iter->second;
}

}  // namespace panda::panda_file

#endif
